
#include <life/options.hpp>
#include <life/lifediscr/functionspace.hpp>
#include <life/lifepoly/im.hpp>
#include <life/lifefilters/gmsh.hpp>
#include <life/lifefilters/gmshtensorizeddomain.hpp>
#include <life/lifevf/vf.hpp>



using namespace Life;
using namespace Life::vf;


inline
po::options_description
makeOptions()
{
    po::options_description code1doptions("Code1D options");
    code1doptions.add_options()
        ("hsize", po::value<double>()->default_value( 0.1 ), "mesh size")
        ("gmsh", po::value<float>()->default_value( 2.1 ), " version of gmsh( 2.0 or 2.1)")
        ;
    return code1doptions.add( Life::life_options() );
}

inline
AboutData
makeAbout()
{
    AboutData about( "Integration" ,
                     "Integration" ,
                     "0.2",
                     "Code 1D for -Delta u = f",
                     Life::AboutData::License_GPL,
                     "Copyright (c) 2010 Universite Joseph Fourier");

    about.addAuthor("Vincent Chabannes", "developer", "vincent.chabannes@imag.fr", "");
    return about;

}


//template<uint Dim,uint N,uint OrderGeo>
class Localisation
  :
  public Application
{
  typedef Application super;
public:
  
  //! numerical type is double
  typedef double value_type;

  //-----------------------------------------------------------------------------------//

  //! geometry entities type composing the mesh
  //  typedef Simplex<Dim,OrderGeo,Dim> convex_type;
  typedef Simplex<2,1,2> convex_type;
  //! mesh type
  typedef Mesh<convex_type> mesh_type;
  //! mesh shared_ptr<> type
  typedef boost::shared_ptr<mesh_type> mesh_ptrtype;

  //-----------------------------------------------------------------------------------//

  //! scalar basis
  typedef bases<Lagrange<5,Scalar> > basis_scalar_type;
  typedef FunctionSpace<mesh_type, basis_scalar_type> space_scalar_type;
  typedef boost::shared_ptr<space_scalar_type> space_scalar_ptrtype;
  typedef  space_scalar_type::element_type element_scalar_type;

  //-----------------------------------------------------------------------------------//

  //Mixte FunctionSpace
  typedef Lagrange<2, Vectorial> basis_u_type;
  typedef Lagrange<4, Scalar> basis_p_type;
  typedef Lagrange<0, Scalar> basis_l_type;
  typedef bases<basis_u_type,basis_p_type, basis_l_type> basis_mixte_type;
  typedef FunctionSpace<mesh_type, basis_mixte_type> space_mixte_type;
  BOOST_MPL_ASSERT( ( boost::is_same< space_mixte_type::bases_list, basis_mixte_type> ) );
  BOOST_MPL_ASSERT( ( boost::is_same< mpl::at< space_mixte_type::bases_list,mpl::int_<0> >::type, basis_u_type> ) );
  BOOST_MPL_ASSERT( ( boost::is_same< mpl::at< space_mixte_type::bases_list,mpl::int_<1> >::type, basis_p_type> ) );
  BOOST_MPL_ASSERT( ( boost::is_same< mpl::at< space_mixte_type::bases_list,mpl::int_<2> >::type, basis_l_type> ) );
  typedef boost::shared_ptr<space_mixte_type> space_mixte_ptrtype;
  // functions 
  typedef space_mixte_type::element_type element_mixte_type;
  typedef element_mixte_type::sub_element<0>::type element_mixte_0_type;
  typedef element_mixte_type::sub_element<1>::type element_mixte_1_type;
  typedef element_mixte_type::sub_element<2>::type element_mixte_2_type;
  
  //-----------------------------------------------------------------------------------//

     
  // Constructor
   
  Localisation( int argc, char** argv, AboutData const& ad,
		po::options_description const& od )
    :
    super( argc, argv, ad, od ),
    meshSize( this->vm()["hsize"].as<double>() )
    {
    }

  // create the mesh using mesh size \c meshSize
  mesh_ptrtype createMesh( double meshSize );

  // run the application
  void run();
  
private:

  // mesh characteristic size
  double meshSize;

}; // Interpolate

Localisation::mesh_ptrtype
Localisation::createMesh( double meshSize ) {
 
    mesh_ptrtype mesh( new mesh_type );

    GmshTensorizedDomain< convex_type::nDim,convex_type::nOrder,convex_type::nRealDim, Simplex> td;
    td.setCharacteristicLength( meshSize );
    td.setX( std::make_pair( 0, 1 ) );
    td.setY( std::make_pair( 0, 1 ) );

    std::string fname = td.generate( convex_type::name().c_str() );

    ImporterGmsh<mesh_type> import( fname );
    mesh->accept( import );

    return mesh;

}

//template<uint Dim,uint N,uint OrderGeo>
void
Localisation::run()
{

  if ( this->vm().count( "help" ) )
    {
      std::cout << this->optionsDescription() << "\n";
      return;
    }

    using namespace Life::vf;  
  //-----------------------------------------------------------------------------------//

    //int MyOrder=N;

  this->changeRepository( boost::format( "%1%/%2%/h_%3%/" )
			  % this->about().appName()
			  % convex_type::name()
			  % this->vm()["hsize"].as<double>()
			  );

  //-----------------------------------------------------------------------------------//

  mesh_ptrtype mesh = createMesh( meshSize );

  //-----------------------------------------------------------------------------------//

  space_scalar_ptrtype Xh = space_scalar_type::New( mesh );

  space_mixte_ptrtype Xh_Mixte = space_mixte_type::New( mesh );


  element_scalar_type us( Xh, "us" );

  element_mixte_type U_mixte(Xh_Mixte,"U_mixte");
  element_mixte_0_type u_mixte = U_mixte.element<0>();
  element_mixte_1_type p_mixte = U_mixte.element<1>();


  //AUTO (g , sin(0.5*M_PI*Px())*(cos(0.5*M_PI*Py())));
  AUTO (g , Px()*(Px()-1)*0.5*Py() );
  //AUTO(g , cst(1.));

  us = project( Xh, elements(mesh), g );

  //-----------------------------------------------------------------------------------//

  BOOST_CHECK(true );

  //std::cout<<"\nContext :" << (idv(u)).context<<"\n";
  std::cout<<"\n------------------------------------";

  std::cout<<"\norder cst(1.0) :" << (cst(1.0)).imorder;

  std::cout<<"\norder id :" << (idv(us)).imorder;
  std::cout<<"\norder grad :" << (gradv(us)).imorder;
  std::cout<<"\norder id*id :" << (idv(us)*idv(us)).imorder;

  std::cout<<"\norder cst+id :" << (cst(1.0)+idv(us)).imorder;
  std::cout<<"\norder id+id :" << (idv(us)+idv(us)).imorder;

  std::cout<<"\norder id+id :" << (idv(us)+idv(p_mixte)).imorder;

  std::cout<<"\norder vec(u) :" << (vec(idv(us),idv(us))).imorder;

  std::cout<<"\norder mat(u) :" << (mat<2,2>(idv(us),cst(1),idv(us),idv(us))).imorder;

  std::cout<<"\norder integrate :" << (integrate(elements(mesh), _Q<3>(), idv(us) ) +integrate(elements(mesh), _Q<3>(), idv(us) ) ).context ;

  std::cout<<"\norder integrate eval :" << (integrate(elements(mesh), _Q<3>(), idv(us) )).evaluate()(0,0);

  std::cout<<"\norder integrate eval :" << (integrate(elements(mesh), idv(us) )).evaluate()(0,0);
  std::cout<<"\norder integrate eval :" << (integrate(elements(mesh), cst(1.) )).evaluate()(0,0);

  std::cout<<"\norder integrate eval :" << (integrate(elements(mesh), exp(Px()) )).evaluate()(0,0);


  std::cout<<"\n";


}


/*
//#define BOOST_TEST_MODULE kdtree tests
//#include <boost/test/unit_test.hpp>
#include <boost/test/included/unit_test.hpp>
//using boost::unit_test::test_suite;
using namespace boost::unit_test;

#include <boost/test/parameterized_test.hpp>

void test(int aaa) { } 
class test_class {
public:
    void test_method1()
    {
        BOOST_CHECK( true  );
    }
    void test_method2()
    {
        BOOST_CHECK( false  );
    }
};





//int main( int argc, char** argv )
//BOOST_AUTO_TEST_SUITE( kdtree_testsuite )

//BOOST_AUTO_TEST_CASE( test_1d_kdtree )

boost::unit_test::test_suite*
init_unit_test_suite( int argc, char** argv )

{

  //boost::unit_test::test_suite* ts = BOOST_TEST_SUITE("test automatic integration order"); 

  //  Localisation moncode( argc, argv, makeAbout(), makeOptions() );
  //boost::shared_ptr<Localisation> moncode( new Localisation ( argc, argv, makeAbout(), makeOptions() ) );
  //boost::unit_test::framework::master_test_suite().add( BOOST_TEST_CASE( boost::bind( &Localisation::run, moncode )));

  boost::shared_ptr<test_class> tester( new test_class );

  boost::unit_test::framework::master_test_suite().
    add( BOOST_TEST_CASE( boost::bind( &test_class::test_method1, tester )));
  boost::unit_test::framework::master_test_suite().
    add( BOOST_TEST_CASE( boost::bind( &test_class::test_method2, tester )));



  return 0;


  //  ts->add(BOOST_TEST_CASE(new Localisation( argc, argv, makeAbout(), makeOptions() ) ) );
  //  ts->add(BOOST_TEST_CASE( & (moncode.run) )); 

  //  std::cout<<boost::unit_test::auto_unit_test_suite()->argc;
  //dim, N, OrderGeo
  //  Localisation moncode( argc, argv, makeAbout(), makeOptions() );

  // moncode.run();
}

//BOOST_AUTO_TEST_SUITE_END() // kdtree test suite

*/

/*
#define BOOST_TEST_MAIN
//# define BOOST_TEST_DYN_LINK 
//#include <boost/testsuite/testsuite.hpp>
//#include <boost/test/included/unit_test.hpp>
#include <boost/test/unit_test.hpp>
#include <boost/test/results_reporter.hpp>
#include <boost/test/output_test_stream.hpp>
#include <boost/test/unit_test_log.hpp>
#include <boost/test/unit_test_suite.hpp>
#include <boost/test/framework.hpp>
#include <boost/test/floating_point_comparison.hpp>

using namespace boost::unit_test;

//_____________________________________________________________________________//

void free_test_function()
{
    BOOST_CHECK( true  );
}

//____________________________________________________________________________//

test_suite*
init_unit_test_suite( int argc, char* argv[] ) 
{
  //    framework::master_test_suite().add( BOOST_TEST_CASE( &free_test_function ) );
    boost::unit_test::test_suite* test = BOOST_TEST_SUITE( "FunctionSpace test suite" );

    //    test->add( BOOST_TEST_CASE( free_test_function   ) );

    return test;
}

*/
/*
//#define BOOST_TEST_DYN_LINK
#include <boost/test/included/unit_test.hpp>
#include <boost/bind.hpp>
using namespace boost::unit_test;
#include <iostream>


//____________________________________________________________________________//

void free_test_function( int i, int j )
{
    BOOST_CHECK( true  );
}

//____________________________________________________________________________//

bool
init_function()
{
    framework::master_test_suite().
        add( BOOST_TEST_CASE( boost::bind( &free_test_function, 1, 1 ) ) );
    framework::master_test_suite().
        add( BOOST_TEST_CASE( boost::bind( &free_test_function, 1, 2 ) ) );
    framework::master_test_suite().
        add( BOOST_TEST_CASE( boost::bind( &free_test_function, 2, 1 ) ) );

    //std::cout << "\n"<< ::boost::unit_test::unit_test_suite()->argc <<"\n";
    std::cout << "\n OOO"<< ::boost::unit_test::framework::master_test_suite().argc <<"\n";
    return true;
}

//____________________________________________________________________________//

int
main( int argc, char* argv[] )
{
    return ::boost::unit_test::unit_test_main( &init_function, argc, argv );
}
*/

#define BOOST_TEST_MODULE kdtree tests
#include <boost/test/unit_test.hpp>
using boost::unit_test::test_suite;

#include <iostream>


BOOST_AUTO_TEST_SUITE( kdtree_testsuite )

BOOST_AUTO_TEST_CASE( test_1d_kdtree )
{

  std::cout << "\n OOO"<< ::boost::unit_test::framework::master_test_suite().argc <<"\n";
  BOOST_CHECK( true  );
  
  //  Localisation moncode( argc, argv, makeAbout(), makeOptions() );

  //moncode.run();

}
BOOST_AUTO_TEST_SUITE_END() // kdtree test suite
